package com.rpc.registry;

import com.rpc.connection.ZkConnection;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.util.List;

//注册器工具
//通过zk链接对象，和传入的Remote 接口实现对象，完成rmi地址拼接，保存到zk
//缺少 locateRegistry 对象，缺少 类型中属性服务，缺少zkconnection 创建过程
public class RpcRegistry {
    //链接对象
    private ZkConnection connection;
    private String ip;
    private int port;

    /**
     * 问题，反复注册会跑出异常  已经存在,需要判断是否存在，如果存在则删除
     * 注册服务方法
     * 1.拼接 rmi 地址
     * 2.把地址存储再zk中
     * @param serviceInterface 服务接口类对象如：com.rpc.service.UserService.class
     *                         //接口必须是Remote接口的子接口
     * @param serviceObject 服务实现类型的对象 如： new com.rpc.service.impl.UserServiceImpl
     *                      实现类型必须实现，必须实现serviceInterface 且是 Remote 接口直接或间接实现类
     * @throws Exception 抛出异常代表注册失败
     */
    public void registerService(Class<? extends Remote> serviceInterface, Remote serviceObject) throws IOException, InterruptedException, KeeperException {
        //rim = rmi://ip:port/com.rpm.service.UserService
        String rmi = "rmi://"+ip+":"+port+"/"+ serviceInterface.getName();
        //拼接一个有规则的zk存储节点命名
        String path = "/rpctest/rpc/"+serviceInterface.getName();

        //todo 可以优化为直接插数据，如果有就直接用里面的cversion删除
        List<String> children = connection.getConection().getChildren("/rpctest/rpc", false);
        if(children.contains(serviceInterface.getName())){
            //需要先查出数据
            Stat stat = new Stat();
            connection.getConection().getData(path, false, stat);
            //删除
            connection.getConection().delete(path, stat.getCversion());
        }


        connection.getConection().create(path, rmi.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        //将服务对象注再rmi 中 registry 中注册
        Naming.rebind(rmi, serviceObject);

    }

    /**
     * 根据服务接口类型，访问zk 获取rmi 远程代理对象
     * @return
     */
    public <T extends Remote> T getServiceProxy(Class<?> serviceInterface) throws IOException, InterruptedException, KeeperException, NotBoundException {
        //拼接zk中的节点名称
        String path = "/rpctest/rpc/"+serviceInterface.getName();
        //查询节点中存储的数据
        byte[] data = connection.getConection().getData(path, false, null);
        //将字节数组，翻译长rmi 访问地址
        String rmi = new String(data);
        //创建代理对象
        Object obj = Naming.lookup(rmi);
        return (T) obj;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public ZkConnection getConnection() {
        return connection;
    }

    public void setConnection(ZkConnection connection) {
        this.connection = connection;
    }
}
